#include "stdafx.h"
#include "ioutils.h"

#include <intrin.h>

namespace IOUtils
{

inline DWORD flipID(DWORD id) {
	return _byteswap_ulong(id);
}

MappedFile::MappedFile() {
	hFile = INVALID_HANDLE_VALUE;
}

MappedFile::~MappedFile() {
	Close();
}

unsigned char* MappedFile::OpenForRead(const TCHAR *filename) {
	hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if (hFile == INVALID_HANDLE_VALUE)
		return 0;
	sz = GetFileSize(hFile,0);
	hFileMapping = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, NULL);
	if (hFileMapping == NULL) {
		CloseHandle(hFile);
		hFile=0;
		return 0;
	}
	return (unsigned char *)MapViewOfFile(hFileMapping, FILE_MAP_READ,
		0, //high 4 bytes
		0, // low 4 bytes
		sz);
}

bool MappedFile::IsOpen() {
	return hFile != INVALID_HANDLE_VALUE;
}

void MappedFile::Close() {
	if (IsOpen()) {
		UnmapViewOfFile(ptr);
		CloseHandle(hFileMapping);
		CloseHandle(hFile);
		hFile = INVALID_HANDLE_VALUE;
	}
}


RIFFFile::RIFFFile() {
	stackptr = 0;
	curpos = 0;
	stack[stackptr] = 0;
	start[stackptr] = 0;
}

bool RIFFFile::Open(const TCHAR *filename) {
	file = mapfile.OpenForRead(filename);
	if (!file)
		return false;
	szstack[stackptr] = mapfile.GetSize();
	return true;
}

// Custom hack, for broken formats like .MID
// Thanks to function level linking, this will not take up space in the 64k
void RIFFFile::FakeOuterBlock() {
	stackptr++;
	stack[stackptr] = 0;
	start[stackptr] = 0;
	szstack[stackptr] = mapfile.GetSize();
}

void RIFFFile::Close() {
	mapfile.Close();
}

bool RIFFFile::Descend(DWORD chunkname, DWORD id) {
	while (curpos < start[stackptr] + szstack[stackptr]) {
		int blockstart = curpos;
		int fid = Read();
		int sz = Read();
		int id2 = Read();

		if (fid != flipID('LIST') && fid != flipID('RIFF'))
			curpos -= 4;

		if (chunkname == flipID(fid) && (id ? id == flipID(id2) : true)) {
			stackptr++;
			szstack[stackptr] = sz;
			start[stackptr] = curpos;
			stack[stackptr] = blockstart + sz + 8;
			return true;
		} else {
			curpos = blockstart + sz + 8;
		}
	}
	return false;
}

// Custom hack, for broken formats like .MID
// Thanks to function level linking, this will not take up space in the 64k
bool RIFFFile::DescendBS(DWORD chunkname, DWORD id) {
	while (curpos < start[stackptr] + szstack[stackptr]) {
		int blockstart = curpos;
		int fid = Read();
		int sz =  _byteswap_ulong(Read());
		int id2 = Read();

		if (fid != flipID('LIST') && fid != flipID('RIFF'))
			curpos -= 4;

		if (chunkname == flipID(fid) && (id ? id == flipID(id2) : true)) {
			stackptr++;
			szstack[stackptr] = sz;
			start[stackptr] = curpos;
			stack[stackptr] = blockstart + sz + 8;
			return true;
		} else {
			curpos = blockstart + sz + 8;
		}
	}
	return false;
}

DWORD RIFFFile::Read() {
	DWORD *ptr = (DWORD *)(file + curpos);
	curpos += 4;
	return *ptr;
}

void RIFFFile::Seek(int offset) {
	curpos = offset;
}

void RIFFFile::Ascend() {
	curpos = stack[stackptr];
	stackptr--;
}

const void *RIFFFile::GetChunkPtr() const {
	return (const void*)(file + curpos);
}

int RIFFFile::GetChunkSize() const {
	return szstack[stackptr];
}

}  // namespace